//--------------------------------------------------------------------------
// 
//  Copyright (c) Chili Software.  All rights reserved. 
// 
//  File: RelationInfoCollection.cs
//
//  Description: Holds all relations of the persistent objects.
// 
//--------------------------------------------------------------------------

using System;
using System.Reflection;
using System.Collections.Generic;
using Chili.Opf3.Relations;
using System.Collections;

namespace Chili.Opf3.Mapping
{
	/// <summary>
	/// Class that represents a collection of <see cref="RelationInfo">RelationInfo</see> objects.
	/// </summary>
	/// <remarks>
	/// This class represents a collection of <see cref="RelationInfo">RelationInfo</see> objects. An instance of this class
	/// is stored as list of all relations with an instance of the <see cref="TypeMapping">TypeMapping</see> class.
	/// </remarks>
	internal sealed class RelationInfoCollection : IRelationInfoCollection
	{
		private Dictionary<Type, TypeRelationInfoCollection> _relationMappings = new Dictionary<Type, TypeRelationInfoCollection>();
		private Dictionary<string, RelationInfo> _attributeRelationMappings = new Dictionary<string, RelationInfo>();
		private List<IRelationInfo> _relations = new List<IRelationInfo>();

		/// <summary>
		/// Adds the <see cref="RelationInfo">RelationInfo</see> object to the list.
		/// </summary>
		/// <param name="info">The object that is added.</param>
		/// <param name="attribute">The <see cref="RelationAttribute">RelationAttribute</see> of the relation.</param>
		/// <param name="genericType">The type of the associated persistent object(s).</param>
		internal void Add(Type genericType, RelationAttribute attribute, RelationInfo info)
		{
			// If no entry for the generic type has found, add a new one.
			if (!_relationMappings.ContainsKey(genericType))
				_relationMappings.Add(genericType, new TypeRelationInfoCollection(genericType));

			// Add the relation mapping.
			_relationMappings[genericType].Add(info);
			_attributeRelationMappings.Add(attribute.Name, info);
			_relations.Add(info);
		}

		/// <summary>
		/// Returns the <see cref="ITypeRelationInfoCollection">ITypeRelationInfoCollection</see> object for the given type.
		/// </summary>
		/// <param name="type">Type of the related object.</param>
		public ITypeRelationInfoCollection GetRelationMapping(Type type)
		{
			TypeRelationInfoCollection info = null;
			_relationMappings.TryGetValue(type, out info);

			return info;
		}

		/// <summary>
		/// Returns the <see cref="IRelationInfo">IRelationInfo</see> object associated with the 
		/// <see cref="RelationAttribute">RelationAttribute</see>'s name.
		/// </summary>
		/// <param name="name">The relation's name. The name is specified by setting 
		/// the <see cref="RelationAttribute">RelationAttribute</see>'s name property.</param>
		public IRelationInfo GetRelationMapping(string name)
		{
			if (name == null)
				throw new ArgumentNullException("name");

			RelationInfo info = null;
			_attributeRelationMappings.TryGetValue(name, out info);

			return info;
		}

		/// <summary>
		/// Determines whether an element is in the collection.
		/// </summary>
		/// <param name="item">The object to locate in the collection.</param>
		/// <returns>True if item is found in the collection; otherwise, false.</returns>
		public bool Contains(IRelationInfo item)
		{
			return _relations.Contains(item);
		}

		/// <summary>
		/// Copies the entire collection to a compatible one-dimensional array, starting at the specified index of the target array.
		/// </summary>
		/// <param name="array">The one-dimensional System.Array that is the destination of the elements copied from collection. The System.Array must have zero-based indexing.</param>
		/// <param name="arrayIndex">The zero-based index in array at which copying begins.</param>
		public void CopyTo(IRelationInfo[] array, int arrayIndex)
		{
			_relations.CopyTo(array, arrayIndex);
		}

		/// <summary>
		/// Gets the number of elements actually contained in the collection.
		/// </summary>
		public int Count
		{
			get { return _relations.Count; }
		}

		/// <summary>
		/// Returns whether the collection is read-only.
		/// </summary>
		public bool IsReadOnly
		{
			get { return true; }
		}

		/// <summary>
		/// Returns the item at the given index.
		/// </summary>
		/// <param name="index">The index of the item that is returned.</param>
		/// <returns>The item at the given index.</returns>
		public IRelationInfo this[int index]
		{
			get { return _relations[index]; }
		}

		/// <summary>
		/// Invalidates all relations in this collection.
		/// </summary>
		/// <param name="obj">The persistent object on which all relations are invalidated.</param>
		public void InvalidateAll(object obj)
		{
			if (obj == null)
				throw new ArgumentNullException("obj");

			// invalidate all relations.
			foreach (IRelationInfo relation in this)
			{
				ObjectHolderBase holder = relation.GetObjectHolder(obj);
				if (holder != null)
					holder.Invalidate();
			}
		}

		#region IEnumerable<IRelationInfo> Members

		/// <summary>
		/// Returns an enumerator that iterates through the collection.
		/// </summary>
		/// <returns> A enumerator that can be used to iterate through the collection.</returns>
		public IEnumerator<IRelationInfo> GetEnumerator()
		{
			return _relations.GetEnumerator();
		}

		#endregion

		#region IEnumerable Members

		/// <summary>
		/// Returns an enumerator that iterates through a collection.
		/// </summary>
		/// <returns>An enumerator object that can be used to iterate through the collection.</returns>
		System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
		{
			return ((IEnumerable)_relations).GetEnumerator();
		}

		#endregion
	}
}
